<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Folder Upload</title>
    <style>
      body {
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
        height: 100vh;
        margin: 0;
        padding: 0;
      }
      #upload-container {
        text-align: center;
        border: 2px dashed #007bff;
        height: 500px;
        width: 80vw;
      }
      #folderInput {
        margin-bottom: 10px;
      }
    </style>
  </head>
  <body>
    <p>
      <a href="/uploads" target="_blank" rel="noopener noreferrer">/uploads</a>
    </p>
    <div
      id="upload-container"
      ondrop="dropHandler(event)"
      ondragover="dragOverHandler(event)"
    >
      <h2>大批量文件上传例子（支持超10w个文件上传）</h2>
      <p>
        <label for="fileInput">上传文件</label>
        <input type="file" id="fileInput" multiple />
      </p>
      <p>
        <label for="folderInput">上传文件夹</label>
        <input
          type="file"
          id="folderInput"
          webkitdirectory
          directory
          multiple
        />
      </p>
      <button onclick="uploadItems()">上传</button>
      <p id="fileNumber"></p>
      <p id="uploadStatus"></p>
    </div>

    <script src="./TaskManager.js"></script>
    <script>
      function uploadItems() {
        const fileInput = document.querySelector("#fileInput");
        const folderInput = document.querySelector("#folderInput");

        let files = [];

        if (fileInput.files.length > 0) {
          files = fileInput.files;
        } else if (folderInput.files.length > 0) {
          files = folderInput.files;
        }

        if (files.length > 0) {
          uploadFiles(files);
        } else {
          console.log("未选择文件或文件夹");
        }
      }

      // 定义一个异步的 dropHandler 函数，用于处理拖放事件
      async function dropHandler(event) {
        // 检查拖拽的数据类型是否包含 "Files"（表示拖拽的是否为文件）
        if (!event.dataTransfer.types.includes("Files")) {
          // 如果不是文件，则不执行任何操作并返回
          return;
        }
        // 阻止事件的默认行为，允许在目标元素上放置文件
        event.preventDefault();

        // 调用 handleFiles 函数处理拖放的文件，并等待其异步完成
        const files = await handleFiles(event);
        // 在控制台打印处理后的文件信息
        console.log("[handleFiles]", files);
        // 调用 uploadFiles 函数上传处理后的文件
        uploadFiles(files);
      }

      const MAX_FILES = Infinity;
      const MAX_DEPTH = Infinity;

      async function traverseDirectory(files, entry, path = "", depth = 0) {
        if (depth > MAX_DEPTH) {
          throw new Error(`文件夹超过${MAX_DEPTH}层，无法上传`);
        }

        if (files.length > MAX_FILES) {
          throw new Error(`文件数超过${MAX_FILES}，无法上传`);
        }

        const dirReader = entry.createReader();
        let entries;

        do {
          entries = await new Promise((resolve) =>
            dirReader.readEntries(resolve)
          );

          for (const ent of entries) {
            if (ent.isFile) {
              const file = await new Promise((resolve) => ent.file(resolve));
              if (!file.webkitRelativePath) {
                file.myRelativePath = ent.fullPath;
              }
              // 如果是文件，直接添加到文件列表
              files.push(file);
            } else if (ent.isDirectory) {
              // 如果是目录，调用traverseDirectory处理目录
              const subFiles = await traverseDirectory(
                files,
                ent,
                `${path}/${ent.name}`,
                depth + 1
              );
            }
          }
          const fileNumberElement = document.querySelector("#fileNumber");

          fileNumberElement.innerHTML = `正在读取文件中，已经读取到${files.length}个文件`;
          console.log("entries", entries);
        } while (entries.length > 0);
      }

      async function handleFiles(event) {
        try {
          const items = Array.from(event.dataTransfer.items);

          const files = [];

          const length = items.length;

          const taskList = [];

          for (let i = 0; i < length; i++) {
            try {
              const item =
                items[i] && items[i].webkitGetAsEntry
                  ? items[i].webkitGetAsEntry()
                  : items[i];
              if (item) {
                if (item.isFile) {
                  const file = items[i].getAsFile();
                  files.push(file);
                } else if (item.isDirectory) {
                  const taskItem = traverseDirectory(files, item);

                  taskList.push(taskItem);
                }
              }
            } catch (err) {
              console.error("[handleFiles err]", i, err);
            }
          }

          await Promise.all(taskList);

          return files;
        } catch (error) {
          console.error("[handleFiles error]", error);
          throw error;
        }
      }

      function dragOverHandler(ev) {
        // 处理拖放过程中的事件，防止默认的文件打开行为
        ev.preventDefault();
      }

      function resetInput() {
        const fileInput = document.querySelector("#fileInput");
        const folderInput = document.querySelector("#folderInput");
        fileInput.value = "";
        folderInput.value = "";
      }

      const manager = new TaskManager(20);

      function handleUpload(taskId, file) {
        // https://developer.mozilla.org/zh-CN/docs/Web/API/FormData
        const formData = new FormData();
        formData.append("files", file);
        formData.append(
          "paths",
          file.myRelativePath || file.webkitRelativePath || file.name
        ); // Add path information
        return fetch("/upload", {
          method: "POST",
          body: formData,
        })
          .then((response) => response.text())
          .then((data) => {})
          .catch((error) => {});
      }

      function uploadFiles(files) {
        files = Array.from(files);
        console.log("files", files);
        // 创建FormData并上传文件
        let counter = 0;
        const fileNumberElement = document.querySelector("#fileNumber");

        const uploadStatusElement = document.querySelector("#uploadStatus");
        const totalFilesNumber = files.length;
        fileNumberElement.innerHTML = `读取到的文件数量为：${totalFilesNumber}`;

        files.forEach((file, index) => {
          try {
            manager
              .add(index, () => handleUpload(index, file), { index })
              .then(() => {
                counter++;
                console.log(
                  "[manager.add 成功上传文件当前索引，累计文件个数]",
                  index,
                  counter
                );
                uploadStatusElement.innerHTML = `
                累计成功文件个数：${counter}<br/>
                剩余文件个数：${totalFilesNumber - counter}<br/>
                上传文件成功的索引：${index}<br/>
                `;
              })
              .catch((err) => {
                console.log(
                  "[manager.add 失败上传文件当前索引，错误信息]",
                  index,
                  err
                );
              });
          } catch (error) {
            console.error("[forEach error]", index, error);
          }
        });
      }
    </script>
  </body>
</html>
